package procs

import (
	"fmt"
	"strings"

	"gitee.com/xuender/monitor/pb"
	"github.com/xuender/oils/base"
	"github.com/xuender/oils/ios"
)

func NewStat() (*pb.Stat, error) {
	stat := &pb.Stat{Cpus: []*pb.Cpu{}}

	return stat, ios.ReadLine("/proc/stat", func(line string) error {
		data := strings.Fields(line)
		var erro error

		switch data[0] {
		case "cpu":
			stat.Cpu, erro = NewCPU(data)
		case "intr":
			stat.Intr, erro = ParseUint64s(data[1:])
		case "ctxt":
			stat.Ctxt, erro = base.ParseInteger[uint64](data[1])
		case "btime":
			stat.Btime, erro = base.ParseInteger[int64](data[1])
		case "processes":
			stat.Processes, erro = base.ParseInteger[uint64](data[1])
		case "procs_running":
			stat.ProcsRunning, erro = base.ParseInteger[uint64](data[1])
		case "procs_blocked":
			stat.ProcsBlocked, erro = base.ParseInteger[uint64](data[1])
		case "softirq":
			stat.Softirq, erro = ParseUint64s(data[1:])
		default:
			var cpu *pb.Cpu
			cpu, erro = NewCPU(data)
			stat.Cpus = append(stat.Cpus, cpu)
		}

		return erro
	})
}

func ParseUint64s(data []string) (nums []uint64, err error) {
	defer func() {
		if rec := recover(); rec != nil {
			err, _ = rec.(error)
		}
	}()

	nums = make([]uint64, len(data))

	for index, str := range data {
		nums[index] = base.Panic1(base.ParseInteger[uint64](str))
	}

	return
}

func NewCPU(data []string) (cpu *pb.Cpu, err error) {
	defer func() {
		if rec := recover(); rec != nil {
			err, _ = rec.(error)
		}
	}()

	cpu = &pb.Cpu{
		User:    base.Panic1(base.ParseInteger[uint64](data[1])),
		Nice:    base.Panic1(base.ParseInteger[uint64](data[2])),
		System:  base.Panic1(base.ParseInteger[uint64](data[3])),
		Idle:    base.Panic1(base.ParseInteger[uint64](data[4])),
		Iowait:  base.Panic1(base.ParseInteger[uint64](data[5])),
		Irrq:    base.Panic1(base.ParseInteger[uint64](data[6])),
		Softirq: base.Panic1(base.ParseInteger[uint64](data[7])),
	}
	// Linux 2.6.11+
	if len(data) > base.Seven {
		cpu.Steal = base.Panic1(base.ParseInteger[uint64](data[8]))
		cpu.Guest = base.Panic1(base.ParseInteger[uint64](data[9]))
		cpu.GuestNice = base.Panic1(base.ParseInteger[uint64](data[10]))
	}

	return
}

func NewCPUByString(line string) (cpu *pb.Cpu, err error) {
	cpu = &pb.Cpu{}
	name := ""

	_, err = fmt.Sscanf(
		line,
		"%s %d %d %d %d %d %d %d %d %d %d",
		&name,
		&cpu.User,
		&cpu.Nice,
		&cpu.System,
		&cpu.Idle,
		&cpu.Iowait,
		&cpu.Irrq,
		&cpu.Softirq,
		&cpu.Steal,
		&cpu.Guest,
		&cpu.GuestNice,
	)

	return
}
